package com.calligraphy.user.web.controller;

import com.alibaba.fastjson.JSONObject;
import com.calligraphy.bases.utils.*;
import com.calligraphy.user.constant.CookieConstant;
import com.calligraphy.user.constant.LoginConstant;
import com.calligraphy.user.constant.RedisConstant;
import com.calligraphy.user.converter.UserFrom2UserConverter;
import com.calligraphy.user.enums.ResultEnum;
import com.calligraphy.user.exception.UserException;
import com.calligraphy.user.from.UserFrom;
import com.calligraphy.user.model.User;
import com.calligraphy.user.service.MailService;
import com.calligraphy.user.service.UserService;
import com.calligraphy.user.utils.*;
import com.calligraphy.user.utils.RedisUtil;
import com.calligraphy.user.vo.passwordVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;

/**
 * @Author hy
 * @Date 2018/12/13 17:57
 **/
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    private static final String USER_QUEUE_DB_KEY = "USER_QUEUE_DB_KEY";
    private static final String TRUE = "true";
    private static final int BAN_TIME = 5;
    private static final Integer ERROR_COUNT = 4;
    private static final String TOKEN_COOKIE = "token";

    @Autowired
    private UserService service;

    @Autowired
    private MailService mailService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedisUtil redisUtil;

    /** ----------------------------注册/验证------------------------------------ */
    @PostMapping("/register")
    public JSONObject register(@Validated UserFrom userFrom,
                               BindingResult bindingResult){
        JSONObject json = new JSONObject();
        //判断注册信息是否正确
        if(bindingResult.hasErrors()){
            throw new UserException(ResultEnum.PARAM_ERROR.getCode(),
                    bindingResult.getFieldError().getDefaultMessage());
        }
        //查找该用户是否已经存在
        User user =  service.findByEmail(userFrom.getEmail());
        if(user == null){
            user = UserFrom2UserConverter.convert(userFrom);
            //与ConcurrentHashMapCache所使用key值保持一致
            String key = AESUtil.encryptData(user.getEmail());
            boolean result = false;
            //TODO 未设置失效时间,暂时使用定时器0点删除
            result = redisUtil.hSet(USER_QUEUE_DB_KEY,key,user);
            if(result){
                mailService.registerNotify(userFrom.getEmail());
                SetMsg.setMessage(json,0,"注册成功! 请前往邮箱激活!");
                return json;
            }else{
                SetMsg.setMessage(json,1,"系统内部错误，请联系管理员!");
                return json;
            }
        }else{
            SetMsg.setMessage(json,1,"注册失败!该邮箱已使用!");
            return json;
        }
    }

    @RequestMapping("/verify")
    public JSONObject verify(String key) {
        JSONObject json = new JSONObject();
        //传输过程丢失了符号
        key = key.replace(" ", "+");
        try {
            String value = (String)ConcurrentHashMapCache.get(key);
            if(StringUtils.isEmpty(value)){
                SetMsg.setMessage(json,1,"激活失败,请确认链接是否过期!");
                return json;
            }else{
                User item = redisUtil.hGet(USER_QUEUE_DB_KEY, key);
                if(item != null){
                    service.registerUser(item);
                    redisUtil.hdel(USER_QUEUE_DB_KEY, key);
                    SetMsg.setMessage(json,0,"恭喜您激活成功");
                    return json;
                }
            }
        }catch (Exception e) {
            e.printStackTrace();
            SetMsg.setMessage(json,1,"系统内部错误，请联系管理员!");
        }finally {
            log.info("【激活成功】");
        }
        return json;
    }

    /** ----------------------------登录/注销------------------------------------ */
    @PostMapping("/login")
    public JSONObject login(@RequestParam(value = "email") String email,
                            @RequestParam(value = "passWord") String passWord,
                            HttpServletRequest req,HttpServletResponse resp
                        ){
        JSONObject json = new JSONObject();
        if(StringUtils.isBlank(email) || StringUtils.isBlank(passWord)){
            SetMsg.setMessage(json,1,"登录失败,账号或密码错误!");
            return json;
        }
        //判断该用户是否注册
        User user = service.findByEmail(email);
        if(user == null){
            SetMsg.setMessage(json,1,"请确认该用户是否在本网站注册!");
            return json;
        }
        //注:直接使用ip是无法作为COOKIE的key值的
        String ip = (IpUtil.getIP(req)).replaceAll("\\:", "");
        log.info("【ip地址为】,ip={}",ip);
        String banObject = redisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_PREFIX, ip));
        if(StringUtils.isEmpty(banObject)){
            log.info("【该ip正常访问】");
        }else{
            JSONObject jsonObject = JSONObject.parseObject(banObject);
            String flag = (String)jsonObject.get("flag");
            String eTime = (String)jsonObject.get("date");
            long l = Long.valueOf(eTime).longValue();
            //计算时间差
            Long between = (System.currentTimeMillis() - l) / (1000 * 60);
            if(TRUE.equals(flag) && (between < BAN_TIME)){
                SetMsg.setMessage(json,1,"账户被禁止登录5分钟，请稍后重试!");
                return json;
            }
        }
        String pwd = HashUtil.encryPassword(passWord);
        User u = service.findByEmailAndPassWord(email,pwd);
        if(u == null){
            Integer expire = RedisConstant.EXPIRE_5M;
            // 从cookie中获取错误次数
            Cookie cookie = CookieUtil.get(req,String.format(CookieConstant.TOKEN_PREFIX, ip));
                Integer error = null;
                if(cookie!=null){
                    error = Integer.valueOf(cookie.getValue());
                }
                error = (error == null) ? 0 : (error + 1);
                if (ERROR_COUNT.equals(error)) {
                    String newJson = String.format("{'flag':'%1$s','date':'%2$s'}", "true",System.currentTimeMillis());
                    redisTemplate.opsForValue().set(
                            String.format(RedisConstant.TOKEN_PREFIX, ip), newJson, expire, TimeUnit.SECONDS);
                }
                CookieUtil.set(resp,String.format(CookieConstant.TOKEN_PREFIX, ip),error.toString(),expire);
                SetMsg.setMessage(json,1,"账号或密码错误!您已输错"+(error+1)+"次你还有" + (4 - error) + "次机会");
                return json;
        }else {
           /* HttpSession session = req.getSession(true);
            session.setAttribute(LoginConstant.USER_ATTRIBUTE,user);*/
            String token = service.createToken(user.getEmail(),user.getUserName());
            log.info("【token为】,token={}"+token);
            Cookie cookie = new Cookie(TOKEN_COOKIE, token);
            resp.addCookie(cookie);
            user.setToken(token); //方便测试
            json.put("userInfo",user);
            json.put("sorry","网站正在开发...");
            SetMsg.setMessage(json,0,"登录成功!正在跳转...");
            return json;
        }

    }

    @RequestMapping("/logout")
    public void logout(HttpServletRequest req,String token){
      /*  HttpSession session = req.getSession(true);
        session.invalidate();*/
        service.invalidate(token);
    }


    /**
     * 用户鉴权
     * @param token
     * @return
     */
    @PostMapping("/auth")
    public String auth(String token,HttpServletResponse resp){
        //各种方法传递cookie都失败了,只好在这里直接存cookie...
        Cookie cookie = service.auth2(token);
        resp.addCookie(cookie);
        return "0";
    }


    /** ----------------------------个人信息------------------------------------ */

    /**
     * 1.能够提供页面信息 2.更新用户信息(UserId)
     * @param req   邮箱暂时不允许修改
     * @param alter 用户更改信息
     * @return
     */
    @PostMapping("/userInfo")
    public JSONObject userInfo(HttpServletRequest req , User alter){
        JSONObject json = new JSONObject();
        if(alter.getEmail() == null){
            return json;
        }
        //修改个人信息
        User user = service.updateUser(alter);
        if(user != null) {
          /*  HttpSession session = req.getSession(true);
            session.setAttribute(LoginConstant.USER_ATTRIBUTE,user);*/
            json.put("result",user);
            SetMsg.setMessage(json,0,"更新成功!");
        }
        return json;
    }

    /**
     * 重置密码
     * @return
     */
    @PostMapping("/resetPassword")
    public JSONObject resetPassword(passwordVO vo){
        JSONObject json = new JSONObject();
        if(!StringUtils.isBlank(vo.getEmail())){
            int i =service.resetPassword(HashUtil.encryPassword(vo.getNewPassword()),vo.getEmail());
            if(i==1){
                SetMsg.setMessage(json,0,"设置密码成功!");
                return json;
            }
        }
        if (StringUtils.isBlank(vo.getUserId()) || StringUtils.isBlank(vo.getPassWord()) ||
                StringUtils.isBlank(vo.getNewPassword()) || StringUtils.isBlank(vo.getConfirmPassword())) {
            SetMsg.setMessage(json,1,"参数有误!");
            return json;
        }
        User user = service.findByUserIdAndPassword(vo.getUserId(),HashUtil.encryPassword(vo.getPassWord()));
        if (user == null){
            SetMsg.setMessage(json,1,"请输入正确的密码再修改!");
            return json;
        }
        if (!vo.getNewPassword().equals(vo.getConfirmPassword())){
            SetMsg.setMessage(json,1,"新密码必须与确认密码一致!");
            return json;
        }
        user.setPassWord(HashUtil.encryPassword(vo.getNewPassword()));
        User nUser = new User();
        BeanUtils.copyProperties(user,nUser);
        //修改密码
        service.updateUser(nUser);
        SetMsg.setMessage(json,0,"修改密码成功!");
        return json;
    }

    /**
     * 忘记密码
     * @param email
     * @return
     */
    @PostMapping("/lostPassword")
    public JSONObject lostPassword(String email){
        JSONObject json = new JSONObject();
        if (!StringUtils.isBlank(email)) {
            mailService.resetPs(email);
            SetMsg.setMessage(json,0,"发送成功!前往邮箱激活!");
        }
        return json;
    }

    /**
     * 验证链接并跳转到重置界面
     * @param key
     * @return
     */
    @RequestMapping("/reset")
    public JSONObject reset(String key){
        JSONObject json = new JSONObject();
        String email = (String)ConcurrentHashMapCache.get(key);
        if(StringUtils.isBlank(email)){
            SetMsg.setMessage(json,0,"重置链接已过期!");
            return json;
        }
        SetMsg.setMessage(json,0,"验证成功!");
        return json;
    }

}
